package AFMAnalysis;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;

import javax.imageio.ImageIO;


public class Control {

	BufferedImage image = null;
	Pixel[][] pixels;
	ArrayList<Blob> blobArray = new ArrayList<Blob>();
	int currID = 0;

	public Control(File file){

		try{
			image = ImageIO.read(file);
		} catch(Exception e){
			System.out.println(e);
		}
		pixels = generatePixels(image);
	}

	public void createBlobs(){

		for(int count = 1; count < this.image.getHeight()-1; count = count + 1){
			for(int count1 = 1; count1 < this.image.getWidth()-1; count1 = count1 + 1){
				this.setBlobID(count1, count);
			}
		}
	}

	public Pixel[][] generatePixels(BufferedImage image){

		int argbInt;
		Pixel[][] pixels = new Pixel[image.getWidth()][image.getHeight()];
		System.out.println(image.getHeight());
		for(int count = 0; count < image.getHeight(); count = count + 1){
			for(int count1 = 0; count1 < image.getWidth(); count1 = count1 + 1){
				argbInt = image.getRGB(count1, count);
				pixels[count1][count] = createPixel(argbInt, count1, count);
			}
		}
		return pixels;
	}

	public Pixel createPixel(int argbInt, int x, int y){

		int red = (argbInt >> 16) & 0xff;
		int green = (argbInt >> 8) & 0xff;
		int blue = (argbInt) & 0xff;
		Color color = new Color(red, green, blue);
		Pixel pixel = new Pixel(false, false, -1, color, null, x, y);
		return pixel;
	}

	public void setBlobID(int x, int y){

		//For each pixel: see if it is white.
		//If it is, then see if the others around it are also white
		//If any of them are, take their blobID
		//If they aren't, then move on
		//If they don't have an ID, create a new one for that pixel.
		if (this.pixels[x][y].color.getRed() > 125 && 
				this.pixels[x][y].color.getGreen() > 125 &&
				this.pixels[x][y].color.getBlue() > 125){

			this.checkAroundPixel(x,y);
		}
	}

	public Pixel[] smallerRange(int x,int y){

		Pixel[] tempPixels = new Pixel[4];
		tempPixels[0] = this.pixels[x-1][y-1];
		tempPixels[1] = this.pixels[x-1][y];
		tempPixels[2] = this.pixels[x][y-1];
		tempPixels[3] = this.pixels[x+1][y-1];
		return tempPixels;
	}

	public Pixel[] largerRange(int x,int y){

		Pixel[] tempPixels = new Pixel[12];
		tempPixels[0] = this.pixels[x-1][y-1];
		tempPixels[1] = this.pixels[x-1][y];
		tempPixels[2] = this.pixels[x][y-1];
		tempPixels[3] = this.pixels[x+1][y-1];
		tempPixels[4] = this.pixels[x-2][y];
		tempPixels[5] = this.pixels[x-2][y-1];
		tempPixels[6] = this.pixels[x-2][y-2];
		tempPixels[7] = this.pixels[x-1][y-2];
		tempPixels[8] = this.pixels[x][y-2];
		tempPixels[9] = this.pixels[x+1][y-2];
		tempPixels[10] = this.pixels[x+2][y-2];
		tempPixels[11] = this.pixels[x+2][y-1];
		return tempPixels;
	}

	public void checkAroundPixel(int x, int y){

		ArrayList<Pixel> reducedIDs = null;
		Pixel[] tempPixels = smallerRange(x,y);
		ArrayList<Pixel> IDs = new ArrayList<Pixel>();
		for(int count = 0; count < tempPixels.length; count = count + 1){
			if(tempPixels[count].pBlobID != -1){
				IDs.add(tempPixels[count]);
				//System.out.println("Found another white pixel with an ID.");
			}
		}
		if(IDs.size() != 0){
			reducedIDs = this.removeDuplicates(IDs);
			//System.out.println("Found at least 1 other white pixel. Reduced.");
		}
		else{
			reducedIDs = IDs;
			//System.out.println("There weren't any other white pixels with IDs");
		}
		if(reducedIDs.size() == 0){
			this.blobArray.add(new Blob(this.pixels[x][y], this.currID, 0));
			this.pixels[x][y].pBlobID = this.currID;
			this.currID = this.currID + 1;
		}
		else if(reducedIDs.size() == 1){
			this.pixels[x][y].pBlobID = reducedIDs.get(0).pBlobID;
			this.goToEnd(this.blobArray.get(reducedIDs.get(0).pBlobID)).next = this.pixels[x][y];
			//System.out.println("Adopted an ID from a previous pixel, and attached to it's blob.");
		}
		else{
			int tempID = 0;
			if(reducedIDs.get(1).pBlobID < reducedIDs.get(0).pBlobID){
				//Take the first pixel, get its ID
				//Set the current pixels ID to that one
				this.pixels[x][y].pBlobID = reducedIDs.get(1).pBlobID;
				//Get the second pixels ID
				//Find its blob
				//set the first pixel.next equal to the second pixels blobs head
				this.goToEnd(this.blobArray.get(reducedIDs.get(1).pBlobID)).next = this.blobArray.get(reducedIDs.get(0).pBlobID).head;
				//reassign all of that blobs IDs
				tempID = reducedIDs.get(0).pBlobID;
				//reassign blobID of blob
				//delete old blob
				this.blobArray.remove(tempID);
				this.reassignBlobID(this.blobArray.get(reducedIDs.get(1).pBlobID), reducedIDs.get(1).pBlobID);
				for(int count = tempID; count < this.blobArray.size(); count++){
					this.reassignBlobID(this.blobArray.get(count), this.blobArray.get(count).head.pBlobID - 1);
				}
				//get that blobs end and set its .next to the current
				this.goToEnd(this.blobArray.get(reducedIDs.get(1).pBlobID)).next = this.pixels[x][y];
			}
			else{
				//Take the first pixel, get its ID
				//Set the current pixels ID to that one
				this.pixels[x][y].pBlobID = reducedIDs.get(0).pBlobID;
				//Get the second pixels ID
				//Find its blob
				//set the first pixel.next equal to the second pixels blobs head
				this.goToEnd(this.blobArray.get(reducedIDs.get(0).pBlobID)).next = this.blobArray.get(reducedIDs.get(1).pBlobID).head;
				//reassign all of that blobs IDs
				tempID = reducedIDs.get(1).pBlobID;
				//reassign blobID of blob
				//delete old blob
				this.blobArray.remove(tempID);
				this.reassignBlobID(this.blobArray.get(reducedIDs.get(0).pBlobID), reducedIDs.get(0).pBlobID);
				for(int count = tempID; count < this.blobArray.size(); count++){
					this.reassignBlobID(this.blobArray.get(count), this.blobArray.get(count).head.pBlobID - 1);
				}
				//get that blobs end and set its .next to the current
				this.goToEnd(this.blobArray.get(reducedIDs.get(0).pBlobID)).next = this.pixels[x][y];
			}
			//decrement currID
			this.currID--;
			//System.out.println("Encountered two blobs, eliminated one of them.");
		}
	}

	public void generatePicture(String name){

		BufferedImage newImage = new BufferedImage(this.pixels.length, this.pixels.length, BufferedImage.TYPE_INT_RGB);
		ArrayList<Integer> colors = new ArrayList<Integer>();
		colors.add(Color.RED.getRGB());
		colors.add(Color.BLUE.getRGB());
		colors.add(Color.GREEN.getRGB());
		colors.add(Color.YELLOW.getRGB());
		colors.add(Color.ORANGE.getRGB());
		for(int count = 0; count < this.pixels.length; count = count + 1){
			for(int count1 = 0; count1 < this.pixels.length; count1 = count1 + 1){
				if(this.pixels[count1][count].pBlobID != 0){
					newImage.setRGB(count1, count, colors.get(this.pixels[count1][count].pBlobID%5));
				}
				else{
					newImage.setRGB(count1, count, Color.BLACK.getRGB());
				}
			}
		}
		try {
			File rendered = new File("C:\\Users\\Ron\\Desktop\\"+ name + ".png");
			ImageIO.write(newImage, "png", rendered);
		} catch (IOException e) {
			System.out.println(e);
		}
	}

	public void generateFinalPicture(String name, File location, boolean perp, boolean par){

		BufferedImage newImage = new BufferedImage(this.pixels.length, this.pixels.length, BufferedImage.TYPE_INT_RGB);

		for(int count = 0; count < this.pixels.length; count = count + 1){
			for(int count1 = 0; count1 < this.pixels.length; count1 = count1 + 1){
				if(this.pixels[count1][count].isPerpendicular == true && perp){
					newImage.setRGB(count1, count, Color.YELLOW.getRGB());
				}
				else if(this.pixels[count1][count].isParallel == true && par){
					newImage.setRGB(count1, count, Color.BLUE.getRGB());
				}
				else{
					newImage.setRGB(count1, count, Color.BLACK.getRGB());
				}
			}
		}
		try {
			File rendered = new File(location.getAbsolutePath(), name + ".png");
			ImageIO.write(newImage, "png", rendered);
		} catch (IOException e) {
			System.out.println(e);
		}
	}

	public ArrayList<Pixel> removeDuplicates(ArrayList<Pixel> data){

		ArrayList<Pixel> reducedData = new ArrayList<Pixel>();
		reducedData.add(data.get(0));
		boolean searching;
		for(int count = 0; count < data.size(); count = count + 1){
			searching = true;
			for(int count1 = 0; count1 < reducedData.size(); count1 = count1 + 1){
				if((data.get(count).pBlobID != reducedData.get(count1).pBlobID) && searching){
					reducedData.add(data.get(count));
					searching = false;
				}
			}
		}

		return reducedData;
	}

	public Pixel goToEnd(Blob blob){

		Pixel curr = blob.head;
		while(curr.next != null){
			curr = curr.next;
		}
		return curr;
	}

	public ArrayList<Blob> reduceBlobs(int minPixels){

		ArrayList<Blob> reducedArrayList = new ArrayList<Blob>();
		for(int count = 0; count < this.blobArray.size(); count = count + 1){
			int length = this.getLength(this.blobArray.get(count));
			if(length >= minPixels){
				reducedArrayList.add(this.blobArray.get(count));
			}
			else{
				this.reassignBlobID(this.blobArray.get(count), 0);
			}
		}
		return reducedArrayList;
	}

	public int getLength(Blob b){

		Pixel curr = b.head;
		int counter = 1;
		while(curr != null){
			counter = counter + 1;
			curr = curr.next;
		}
		return counter;
	}

	public void reassignBlobID(Blob b, int desired){

		Pixel curr = b.head;
		while(curr.next != null){
			curr.pBlobID = desired;
			curr = curr.next;
		}
		curr.pBlobID = desired;
		b.blobID = desired;

	}

	double roundTwoDecimals(double d) {
		DecimalFormat twoDForm = new DecimalFormat("#.###");
		return Double.valueOf(twoDForm.format(d));
	}

	public void getRadialDistribution(Solver solver, File location, String name, boolean create){

		solver.actualRadialDistribution(blobArray, 500, 12);
		double[] values = solver.getRValues();
		double[] theoreticalRValues = solver.getTheoreticalRValues();
		double[] theoreticalFreqs = solver.getTheoreticalFreqs();
		double[] freqs = solver.getFreqs();
		double[] movingAverage = solver.movingAverage(2);
		System.out.println(values.length);
		double[] idealValues = solver.idealRadialDistribution(values.length);
		if(create){
			try {
				File file = new File(location.getAbsolutePath(), name + "1.txt");
				BufferedWriter out = new BufferedWriter(new FileWriter(file));
				out.write("Count, Actual r, g(r), Moving Average, Theoretical r, g(r)");
				out.newLine();
				for(int count = 0; count < values.length; count++){
					if(theoreticalRValues.length > count){
						out.write(count + ", " + values[count] + ", " + freqs[count] + ", " + movingAverage[count] + ", " + theoreticalRValues[count] + ", " + theoreticalFreqs[count]);
					}
					else{
						out.write(count + ", " + values[count] + ", " + freqs[count] + ", " + movingAverage[count]);
					}
					out.newLine();
				}
				out.close();
			} catch (IOException e){
				e.printStackTrace();
			}
		}
	}
}